package com.jsyl.lwbk.common.utils.pdf;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import org.jsoup.Jsoup;

import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author xiaoshu
 * @date 2020-12-26 17:10
 * @desc html转PDF工具类
 **/
public class HtmlToPDFUtils {

    public static void main(String[] args) throws Exception {
        String htmlPath = "D:/desktop/new/templateNew.html";
        Map<String, String> map = new HashMap<>();
        map.put("name", "xiaoshu");
        String filePath = "D:/desktop/new/xiaoshu.pdf";
        generatePdf(htmlPath, map, filePath);
    }

    /**
     * html模板生成PDF文件输出
     *
     * @param htmlPath html模板文件路径
     * @param map      要替换的字段值
     * @param filePath 输出文件地址
     * @throws Exception
     */
    public static void generatePdf(String htmlPath, Map<String, String> map, String filePath) throws Exception {
        Document document = null;
        InputStream in = null;
        OutputStream outputFile = new FileOutputStream(filePath);
        try {
            // 获取html String形式
            String htmlStr = operateHtml(htmlPath, map);
            // 创建输入html流
            in = stringToInputStream(htmlStr);
            // 第一步
            document = new Document();
            // 第二步
            PdfWriter writer = PdfWriter.getInstance(document, outputFile);
            // 第三步
            document.open();
            // 第四步  解决中文支持问题，中文不显示则给不显示的标签加上样式font-family:'SimSun';
            XMLWorkerHelper.getInstance().parseXHtml(writer, document, in, Charset.forName("UTF-8"));
            // 第五步
            in.close();
            document.close();
        } catch (IOException e) {
            throw e;
        } catch (DocumentException e) {
            throw e;
        } finally {
            if (in != null) {
                in.close();
            }
            if (document != null) {
                document.close();
            }
        }
    }

    /**
     * String转InputStream
     *
     * @param str
     * @return
     */
    private static InputStream stringToInputStream(String str) {
        InputStream in = new ByteArrayInputStream(str.getBytes());
        return in;
    }

    /**
     * InputStream转String
     *
     * @param in
     * @return
     */
    @SuppressWarnings("unused")
    private static String inputStreamToString(InputStream in) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int i = 1;
        try {
            while ((i = in.read()) != -1) {
                baos.write(i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return baos.toString();
    }

    /**
     * 读取html模板文件并替换参数值
     *
     * @param htmlPath html文件路径
     * @param map      要替换的参数值
     * @return
     * @throws Exception
     */
    public static String operateHtml(String htmlPath, Map<String, String> map) throws Exception {
        // html字符串
        String htmlStr = "";
        File htmlFile = null;
        try {
//            htmlFile = new File(htmlPath);
//
//            //获取htmlStr jsoup,建议使用，会补全缺失的</**>
//            org.jsoup.nodes.Document html = Jsoup.parse(htmlFile, "UTF-8");
            htmlStr =readToString(htmlPath);

            // 解决中文问题
            htmlStr = htmlStr.replaceAll("font-family:[^;]*;", "font-family:'SimSun';");

            // 解决docx中文问题
            htmlStr = htmlStr.replaceAll("width:[^;]*;", "");
            htmlStr = htmlStr.replaceAll("margin-bottom:[^;]*;", "");
            htmlStr = htmlStr.replaceAll("margin-left:[^;]*;", "");
            htmlStr = htmlStr.replaceAll("margin-right:[^;]*;", "");
            htmlStr = htmlStr.replaceAll("margin-top:[^;]*;", "");
            htmlStr = htmlStr.replaceAll("p\\.[^\\}]*\\}", "");
            htmlStr = htmlStr.replaceAll("span\\.[^\\}]*\\}", "");
            htmlStr = htmlStr.replaceAll("<p>", "<p style = \"font-family:'SimSun';\">");

            //将未闭合的<meta>标签 闭合
            htmlStr = expectedClosingTag(htmlStr, "(<META[^>]*[^/]>)|(<meta[^>]*[^/]>)");

            //表格线条错误粗修改
            htmlStr = htmlStr.replaceAll("thin solid black", "1");

            //替换特殊字段   字段形式 如： ${test}
            for (Map.Entry<String, String> entry : map.entrySet()) {
                htmlStr = htmlStr.replaceAll("\\$\\{" + entry.getKey() + "\\}", entry.getValue());
                htmlStr = htmlStr.replaceAll("\\$\\{[^\\}]*" + entry.getKey() + "[^\\}]*\\}", entry.getValue());
            }

            // 将未赋值的去除
            htmlStr = htmlStr.replaceAll("\\$\\{[^\\}]*[^\\}]*\\}", "&nbsp;&nbsp;&nbsp;&nbsp;");
            // 将body标签内样式去掉，并加上font-family:'SimSun';解决中文不显示问题
            htmlStr = htmlStr.replaceAll("<body[^>]*>", "<body style=\"font-family:'SimSun';\">");
        } catch (Exception e) {
            throw e;
        } finally {
            if (htmlFile != null) {
                htmlFile.delete();
            }
        }
        return htmlStr;
    }

    /**
     * 处理html meta标签，使meta标签闭合
     *
     * @param htmlStr
     * @param regex
     * @return
     */
    private static String expectedClosingTag(String htmlStr, String regex) {
        Pattern p = Pattern.compile(regex);
        Matcher m = p.matcher(htmlStr);
        List<String> result = new ArrayList<String>();
        while (m.find()) {
            result.add(m.group());
        }
        for (String str : result) {
            int strLen = str.length();
            if (str != null && strLen > 1) {
                htmlStr = htmlStr.replace(str, str.substring(0, strLen - 1) + "/>");
            }
        }
        return htmlStr;
    }

    private static String readToString(String fileName) {
        String encoding = "UTF-8";
        File file = new File(fileName);
        Long filelength = file.length();
        byte[] filecontent = new byte[filelength.intValue()];
        try {
            FileInputStream in = new FileInputStream(file);
            in.read(filecontent);
            in.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            return new String(filecontent, encoding);
        } catch (UnsupportedEncodingException e) {
            System.err.println("The OS does not support " + encoding);
            e.printStackTrace();
            return null;
        }
    }

}
